//编译器
//递归遍历dom树
//判断节点类型，如果是文本，则正则判断是否为插值绑定
//如果是元素，则遍历其属性判断是否是指令，然后递归子元素
class Compile {
    //el是宿主元素
    //vm是KVue实例
    constructor(el, vm) {
        this.$vm = vm
        this.$el = document.querySelector(el)

        if(this.$el) {
            //执行编译
            this.compile(this.$el)
        }
    }

    compile(el) {
        const childNodes = el.childNodes;
        Array.from(childNodes).forEach(node => {
            if(this.isElement(node)) {
                // console.log('编译元素' + node.nodeName);
                this.compileElement(node)
            } else if(this.isInter(node)) {
                // console.log('编译插值绑定' + node.textContent);
                this.compileText(node)
            }

            //递归编译子节点
            if(node.childNodes && node.childNodes.length > 0) {
                this.compile(node)
            }
        })

    }

    //nodeType获取节点类型

    //当前节点是否为元素节点
    isElement(node) {
        return node.nodeType === 1
    }

    //当前节点是否为文本节点
    isInter(node) {
        //首先是文本标签，其次内容是{{xxx}} 加括号是为了用RexExp.$1得到中间值
        return node.nodeType === 3 && /\{\{(.*)\}\}/.test(node.textContent)
    }

    compileText(node) {
        // node.textContent = this.$vm[RegExp.$1]
        this.update(node, RegExp.$1, 'text')
    }

    compileElement(node) {
        //  节点是元素
        //  遍历其属性列表
        const nodeAttrs = node.attributes
        Array.from(nodeAttrs).forEach(attr => {
            const attrName = attr.name
            const exp = attr.value
            if(this.isDirective(attrName)) {
                const dir = attrName.substr(2)
                this[dir] && this[dir](node, exp)
            }
        })
    }

    isDirective(attrName) {
        return attrName.indexOf('k-') === 0
    }

    update(node, exp, dir) {
        //初始化 指令对应更新函数xxUpdater
        const fn = this[dir + 'Updater']
        fn && fn(node, this.$vm[exp])

        //更新处理 封装一个更新函数，可以更新对应的dom元素
        new Watcher(this.$vm, exp, function (val) {
            fn && fn(node, val)
        })
    }

    textUpdater(node, value) {
        node.textContent = value
    }

    htmlUpdater(node, value) {
        node.innerHTML = value
    }

    text(node, exp) {
        // node.textContent = this.$vm[exp]
        this.update(node, exp, 'text')
    }

    html(node, exp) {
        // node.innerHTML = this.$vm[exp]
        this.update(node, exp, 'html')
    }

    // htmlUpdate(node, exp) {
    //
    // }

}